﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System.Threading;
using Landscape.Business;
using Landscape.Business.FNH;
using Landscape.Business.FNH.Activities;

namespace Landscape.WebApp.SignalR
{
    public class Scribe
    {
        private readonly static Lazy<Scribe> myObject = new Lazy<Scribe>(() => new Scribe(GlobalHost.ConnectionManager.GetHubContext<ScribeSignalHub>().Clients));

        private readonly ConcurrentDictionary<int, ScribeItem> scribeLog = new ConcurrentDictionary<int, ScribeItem>();
        private readonly object scribeUpdateLock = new object();


        private Scribe(IHubConnectionContext pClients)
        {
            // TODO: Complete member initialization
            this.Clients = pClients;
            this.scribeLog.Clear();
        }

        public static Scribe Instance
        {
            get { return myObject.Value; }
        }

        private IHubConnectionContext Clients
        {
            get;
            set;
        }

        public IList<ScribeItem> GetScribeLogAfter(int pLastItemID, Guid pIncID)
        {
            if (pLastItemID < 0)
                pLastItemID = 0;

            if (this.scribeLog.Where(exp => exp.Value.IncidentCode == pIncID).Count() == 0)
            {
                using (var nhSession = NHSessionManager.GetManager().GetSession())
                {
                    var erActs = new EmergencyActivities(nhSession);
                    var inci = erActs.GetIncidentByCode(pIncID);
                    if (inci.ScribeLog != null)
                    {
                        foreach (var item in inci.ScribeLog.OrderBy(exp=>exp.ScribeIndex))
                        {
                            ScribeItem scribeItem = new ScribeItem();
                            scribeItem.CreatedBy = item.UpdatedBy;
                            scribeItem.IncidentCode = pIncID;
                            scribeItem.ItemTimeStamp = item.CreatedDate;
                            scribeItem.Message = item.Message;
                            scribeItem.MessageType = item.MessageType.ToString();
                            scribeItem.ShortItemName = item.ShortItemName;
                            scribeItem.ScribeItemCode = item.ID;

                            this.scribeLog.TryAdd((this.scribeLog.Count > 0 ? this.scribeLog.Max(exp => exp.Key) + 1 : 1), scribeItem);
                        }
                    }
                }
            }

            return this.scribeLog.Where(exp => exp.Key > pLastItemID && exp.Value.IncidentCode == pIncID).Select(exp => exp.Value).ToList();
        }

        public void AddToScribeLog(ScribeItem pItem)
        {
            lock (scribeUpdateLock)
            {
                var nextMaxId = (this.scribeLog.Count > 0 ? this.scribeLog.Max(exp => exp.Key) + 1 : 1);
                pItem.ItemTimeStamp = DateTime.Now;

                pItem.ScribeItemCode = SaveToDataBase(pItem);
                this.scribeLog.TryAdd(nextMaxId, pItem);

                BroadcastToClients(pItem);
            }
        }

        private void BroadcastToClients(ScribeItem pItem)
        {
            this.Clients.Group(pItem.IncidentCode.ToString()).updateScribeLog(pItem);

        }

        private Guid SaveToDataBase(ScribeItem pItem)
        {
            Guid id = Guid.Empty;

            using (var nhSession = NHSessionManager.GetManager().GetSession())
            {
                using (var trans = nhSession.BeginTransaction())
                {
                    var erActs = new EmergencyActivities(nhSession);
                    var inci = erActs.GetIncidentByCode(pItem.IncidentCode);

                    Landscape.Business.Entities.Scribe curScribe = new Business.Entities.Scribe();
                    curScribe.CreatedBy = pItem.CreatedBy;
                    curScribe.CreatedDate = pItem.ItemTimeStamp;
                    curScribe.Message = pItem.Message;
                    curScribe.UpdatedBy = pItem.CreatedBy;
                    curScribe.UpdatedDate = pItem.ItemTimeStamp;
                    curScribe.ShortItemName = pItem.ShortItemName;

                    if (inci.ScribeLog != null && inci.ScribeLog.Count > 0)
                    {
                        curScribe.ScribeIndex = inci.ScribeLog.Count + 1;
                    }
                    else
                    {
                        curScribe.ScribeIndex = 1;
                    }

                    curScribe.MessageType = (ScribeType)Enum.Parse(typeof(ScribeType), pItem.MessageType);

                    inci.ScribeLog.Add(curScribe);

                    erActs.SaveIncident(inci);

                    id = erActs.GetIncidentByCode(inci.ID).ScribeLog.Where(exp => exp.ScribeIndex == curScribe.ScribeIndex).Single().ID;

                    trans.Commit();

                    return id;
                }
            }
        }

        internal void ClearEntriesFor(Guid incID)
        {
            var keys = this.scribeLog.Where(exp => exp.Value.IncidentCode == incID).Select(exp => exp.Key).ToArray();;
            ScribeItem itemRemoved = new ScribeItem();
            foreach (var key in keys)
            {
                this.scribeLog.TryRemove(key, out itemRemoved);
            }
        }
    }
}